Skip to content

.NET: Add MapAGUI overload that resolves the agent per request via a factory - #6251

Open
Steven Molen (darthmolen) wants to merge 1 commit into
microsoft:mainfrom
darthmolen:feature/mapagui-factory-delegate
Open

.NET: Add MapAGUI overload that resolves the agent per request via a factory#6251
Steven Molen (darthmolen) wants to merge 1 commit into
microsoft:mainfrom
darthmolen:feature/mapagui-factory-delegate

Conversation

@darthmolen

Copy link
Copy Markdown

Summary

Adds a MapAGUI overload that resolves the agent per request via a factory delegate, instead of capturing a single AIAgent at startup from the root service provider.

public static IEndpointConventionBuilder MapAGUI(
    this IEndpointRouteBuilder endpoints,
    string agentName,
    [StringSyntax("route")] string pattern,
    Func<IServiceProvider, string, AIAgent> createAgentDelegate)

Motivation

The existing MapAGUI overloads resolve one keyed AIAgent once at map time (endpoints.ServiceProvider.GetRequiredKeyedService<AIAgent>(name)). Hosts that must build the agent per request — for per-request authentication, scoped tool/MCP sessions, or conversation-scoped configuration — have no supported entry point and must work around it (e.g. a singleton facade AIAgent that re-resolves a real agent on each run).

This overload invokes the factory once per request with the request's IServiceProvider (HttpContext.RequestServices) and the agent name, so request-scoped services resolve correctly. It mirrors the existing AddAIAgent(name, Func<IServiceProvider, string, AIAgent>) delegate shape.

Details

  • The keyed AgentSessionStore lookup and the ThreadId trust model are unchanged; the store is resolved per request from HttpContext.RequestServices using agentName as the key.
  • The shared request-handling body is factored into a private HandleRunAsync reused by both the instance and factory overloads (no behavior change to the existing path).
  • Argument null-checks for endpoints, agentName, and createAgentDelegate; the factory returning null throws a clear InvalidOperationException.

Tests

Adds unit tests: the overload maps an endpoint; it defers resolution (factory not invoked and no keyed AIAgent resolved at map time, unlike the startup-capture overloads); and a null-factory guard. All 38 tests in Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests pass on net8.0/net9.0/net10.0.

Closes #2988
Related to #5209

Copilot AI review requested due to automatic review settings June 1, 2026 21:05
@moonbox3 Evan Mattson (moonbox3) added the .NET Usage: [Issues, PRs], Target: .Net label Jun 1, 2026
@github-actions github-actions Bot changed the title Add MapAGUI overload that resolves the agent per request via a factory .NET: Add MapAGUI overload that resolves the agent per request via a factory Jun 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new MapAGUI overload that creates an AIAgent per request via a factory delegate, and refactors the shared request-handling logic into a helper to reduce duplication.

Changes:

  • Added MapAGUI overload accepting Func<IServiceProvider, string, AIAgent> for per-request agent resolution.
  • Refactored endpoint handler body into HandleRunAsync and reused it from existing overload(s).
  • Added unit tests for the new overload (mapping, deferral behavior, and null delegate guard).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs Adds new unit tests targeting the new factory-delegate MapAGUI overload and its mapping-time behavior.
dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs Introduces the per-request factory-delegate overload and extracts shared request processing into HandleRunAsync.

@darthmolen

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@hansmbakker

Hans Bakker (hansmbakker) commented Jun 2, 2026

Copy link
Copy Markdown

This would be helpful! I had to add IServiceScopeFactory scopeFactory to my tools to be able to use scopes with Entity Framework in them, and if I understand this PR correctly that workaround would become redundant then.

{
return Results.BadRequest();
}
var aiAgent = createAgentDelegate(context.RequestServices, agentName)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when the factory creates a new ChatClientAgent per request without explicitly setting ChatClientAgentOptions.Id? AIAgent.Id defaults to a new GUID per instance, and the built-in session stores key by (agent.Id, conversationId), so the same AG-UI ThreadId will miss the previously saved session on the next request. Could this overload preserve a stable hosted identity for agentName, or fail fast/document an explicit stable-ID requirement before session persistence silently resets every turn?

…via a factory

Adds a per-request factory-delegate overload of MapAGUIServer for agents that must
be built with request-scoped state (per-request auth, scoped tool/MCP sessions,
conversation-scoped config) rather than a single instance captured at startup.

Preserves stable session identity: AgentSessionStore keys persisted sessions by
(agent.Id, conversationId), and AIAgent.Id defaults to a per-instance GUID. A
per-request factory would therefore compute a different session key every turn and
never find the previously saved session -- silently resetting persistence. The
overload wraps the per-request agent in a StableIdentityAIAgent whose Id is the
logical agentName, so the session key stays constant across requests, matching the
startup-capture overloads. Both overloads now share BuildHostAgent (isolation-store
wiring) and HandleRunAsync.

Tests: factory mapping/deferral/null-arg unit tests; a stable-id assertion; a
two-turn persistence round-trip through the real InMemoryAgentSessionStore and a
negative control proving raw per-request instances lose the session; plus
integration tests asserting per-request invocation and the null-factory failure.
@darthmolen

Copy link
Copy Markdown
Author

Evan Mattson (@moonbox3) Great catch — confirmed and fixed. AIHostAgent keys the session store by InnerAgent.Id and AIAgent.Id defaults per-instance, so a per-request factory reset persistence every turn. The overload now wraps the per-request agent in a StableIdentityAIAgent (Id = agentName), keeping the session key stable across requests — matching the startup-capture overloads. While here I also rebased onto current main and retargeted to MapAGUIServer (the PR had gone conflict-dirty against the AG-UI SDK externalization). Added stable-id + two-turn persistence round-trip tests (with a negative control) and integration coverage; both suites green locally. Happy to switch to a fail-fast on a non-stable id instead of the implicit facade if you'd prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ag-ui Usage: [Issues, PRs], Target: AG-UI protocol integration .NET Usage: [Issues, PRs], Target: .Net

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

.NET: Support dynamic agent resolution in AG-UI endpoints (MapAGUI with factory delegate)

4 participants